//
//  XPCrashLogManager.m
//  PywCommonSDKDemo
//
//  Created by Ampaw on 2018/4/16.
//  Copyright © 2018年 Ampaw. All rights reserved.
//

#import "XPCrashLogManager.h"
#import "XPCrashLogUtils.h"
#import "XPUncaughtExceptionHandler.h"

#import "YYFTAFNetworking.h"
#import "PywCommonDataDealUtils.h"
#import "PywCommonPlatformInfo.h"
#import "PywCommonDeviceInfo.h"

@interface XPCrashLogManager()
@property (nonatomic,assign) BOOL isOpen;
@end

@implementation XPCrashLogManager

static XPCrashLogManager *manager = nil;
+ (XPCrashLogManager *)manager
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [[XPCrashLogManager alloc] init];
    });
    return manager;
}

#pragma mark - Public
/**
 注册异常捕获监听,并设置调试模式
 
 @param isOpen 是否开启提示框展示异常
 */
- (void)InstallUncaughtExceptionHandlerWithIsOpenDebug:(BOOL)isOpen
{
    XPInstallUncaughtExceptionHandler();
    self.isOpen = isOpen;
    
    // 开启一个线程，进行数据异步请求上传app异常日志
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self uploadException];
    });
}

/**
 是否开启提示框
 
 @return YES: 开启，NO: 关闭
 */
- (BOOL)isOpenShowAlertView
{
    return self.isOpen;
}

/**
 存储当前异常数据
 
 @param name     异常名称
 @param reason   异常原因
 @param userInfo 异常信息
 */
- (void)saveExceptionWithName:(NSString *)name
                       Reason:(NSString *)reason
                     UserInfo:(NSDictionary *)userInfo
{
    /**
     * userInfo = "{"XPUncaughtExceptionHandlerStackInfoKey" : ["0 CoreFoundation 0x000000018117f17c <redacted> + 148"],
                    "XPUncaughtExceptionHandlerAppInfoKey" :   "Bundle ID:com.guibei.batianchuanqi  应用版本:6.0(1)  设备机型:iPhone  系统版本:11.2.6"
                   }"
     * 参数说明
     *    应用信息Key:  XPUncaughtExceptionHandlerAppInfoKey
     *    异常信息Key:  XPUncaughtExceptionHandlerStackInfoKey
     */
    NSString *userInfoStr = [self toJSONData:userInfo];
    NSMutableDictionary *exceptionData = [NSMutableDictionary dictionary];
    [exceptionData setObject:name forKey:@"ExceptionType"];
    [exceptionData setObject:reason forKey:@"ExceptionReason"];
    [exceptionData setObject:userInfoStr forKey:@"ExceptionInfo"];
    
    // 格式: {"time":urlencode(pywencode("需加密字符串"),"msg":urlencode(pywencode("需加密字符串"))}
    NSString *time = [self firstPywEncodeAfterUrlEncode:[self getNowTimeTime]];
    NSString *msg = [self firstPywEncodeAfterUrlEncode:[self toJSONData:exceptionData]];
    
    NSMutableDictionary *crashData = [NSMutableDictionary dictionary];
    [crashData setObject:time forKey:@"time"];
    [crashData setObject:msg forKey:@"msg"];
    //新增错误日志x字段
    NSString *pkg = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];;
    NSString *channelid = [[PywCommonPlatformInfo sharedCommonPlatformInfo] channel_id];
    NSString *sdkver = [[PywCommonPlatformInfo sharedCommonPlatformInfo] ver];
    NSString *gamekey = [[PywCommonPlatformInfo sharedCommonPlatformInfo] gamekey];
    NSString *promocode = [[PywCommonPlatformInfo sharedCommonPlatformInfo] promoCode];
    NSString *promochannel = [[PywCommonPlatformInfo sharedCommonPlatformInfo] promoChannel];
    NSString *model = [PywCommonDeviceInfo sharedCommonDeviceInfo].model;
    [crashData setObject:@"iOSException" forKey:@"keyword"];
    [crashData setObject:pkg forKey:@"pkg"];
    [crashData setObject:channelid forKey:@"channelid"];
    [crashData setObject:sdkver forKey:@"sdkver"];
    [crashData setObject:gamekey forKey:@"gamekey"];
    [crashData setObject:promocode forKey:@"promocode"];
    [crashData setObject:promochannel forKey:@"promochannel"];
    [crashData setObject:model forKey:@"model"];
    [XPCrashLogUtils saveCrashLogWithJSONData:crashData];
}

/**
 上传缓存中异常数据
 */
- (void)uploadException
{
    NSArray *crashs = [XPCrashLogUtils readCrashData];
    if (crashs.count > 0) {
        [self requestUploadErrorMessageList:crashs Success:^(NSDictionary *dict) {
            
            int code = [[dict objectForKey:@"ack"] intValue];
            NSString *msg = [dict objectForKey:@"msg"];
            NSLog(@"异常数据上报情况，code:%d , Msg:%@",code,msg);
            if (code == 200) {
                
                // 删除缓存中异常数据【备注：异常上传成功，删除所有异常缓存数据】
                [XPCrashLogUtils deleteCrashLog];
            }
        } Failer:^(NSError *error) {
            
            // 异常上传失败！
            NSLog(@"Error: %@",error);
        }];
    }else{
        NSLog(@"No Statistical Exception Data ！");
    }
}

#pragma mark - Other
// 格式：urlencode(pywencode("需加密字符串")
- (NSString *)firstPywEncodeAfterUrlEncode:(NSString *)text
{
    // pywencode 加密【服务端要求编码格式为：NSISOLatin1StringEncoding 统一】
    // 1. 转成 Data
    NSData *tmpData = [text dataUsingEncoding:NSISOLatin1StringEncoding];
    // 2. 自定义加密
    NSData *testData = [PywCommonDataDealUtils encode:tmpData];
    // 3. 转成 String
    NSString *paramData = [[NSString alloc] initWithData:testData encoding:NSISOLatin1StringEncoding];
    
    // urlencode编码
    NSString *urlencodeStr = [self URLEncodeString:paramData];
    return urlencodeStr;
}

#pragma mark - Request
- (void)requestUploadErrorMessageList:(NSArray *)messageList
                              Success:(void (^)(NSDictionary *dict))success
                               Failer:(void (^)(NSError *error))failer;
{
    // 拼接请求Url地址
    NSString *urlStr = @"http://dt.pyw.cn/Sdk/sdkGameCrashDataCollect";
    
    // 参数处理
    if (messageList.count == 0) return;
    NSData * jsonData = [NSJSONSerialization dataWithJSONObject:messageList options:kNilOptions error:nil];

    YYFTAFHTTPRequestSerializer<YYFTAFURLRequestSerialization> *httpRequestSerializer = [YYFTAFHTTPRequestSerializer serializer];
    httpRequestSerializer.stringEncoding = NSUTF8StringEncoding;
    [httpRequestSerializer setTimeoutInterval:30];
    
    NSMutableURLRequest *request = [httpRequestSerializer
                                    multipartFormRequestWithMethod:@"POST"
                                    URLString:urlStr
                                    parameters:nil
                                    constructingBodyWithBlock:nil
                                    error:nil];
    //设置请求参数
    [request setHTTPBody:jsonData];
    
    YYFTAFHTTPSessionManager *sessionManager = [YYFTAFHTTPSessionManager manager];
    sessionManager.responseSerializer = [YYFTAFHTTPResponseSerializer serializer];
    [[sessionManager dataTaskWithRequest:request completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
        NSDictionary *resultDict = [NSDictionary new];
        if (!error && responseObject) {
            NSError *pError = nil;
            NSData *decodeData = [PywCommonDataDealUtils decode:responseObject];
            if (decodeData) {
                // 解析数据
                resultDict = [NSJSONSerialization JSONObjectWithData:decodeData
                                                             options:NSJSONReadingMutableContainers error:&pError];
                if (pError) {
                    NSLog(@"\n---------- Error Des : %@ ----------", pError.description);
                    NSLog(@"\n---------- ResponseDes : %@ ----------", response.description);
                    return;
                }
            }else{
                NSString *errorMsg = @"A PHP Error Was Encountered, Undefined Configuration Parameters !";
                NSLog(@"\n---------- %@ ----------",errorMsg);
            }
            // 回调数据
            success(resultDict);
        }else{
            NSLog(@"网络请求错误提示：\n%@",error);
            failer(error);
        }
    }] resume];
}

#pragma mark - Data Conversion
/// 获取当前时间戳（以毫秒为单位）
- (NSString *)getNowTimeTime{
    //设置你想要的格式,hh与HH的区别:分别表示12小时制,24小时制
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init] ;
    [formatter setDateStyle:NSDateFormatterMediumStyle];
    [formatter setTimeStyle:NSDateFormatterShortStyle];
    [formatter setDateFormat:@"YYYY-MM-dd HH:mm:ss:SSS"];
    
    //设置时区,这个对于时间的处理有时很重要
    NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:@"Asia/Shanghai"];
    [formatter setTimeZone:timeZone];
    
    //当前时间
    NSString *timeStr = [formatter stringFromDate:[NSDate date]];
    NSLog(@"%@",timeStr);
    
//    //转换为毫秒时间戳
//    NSTimeInterval timeInterval = [datenow timeIntervalSince1970];
//    UInt64 timestamp = timeInterval * 1000;
//    return [NSString stringWithFormat:@"%lld", timestamp];
    return timeStr;
}

/**
 将字典或者数组转化为JSON串

 @param theData 字典或数组
 @return JSON串
 */
- (NSString *)toJSONData:(id)theData{
    NSError *error = nil;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:theData
                                                       options:NSJSONWritingPrettyPrinted
                                                         error:&error];
    
    if ([jsonData length] > 0 && error == nil){
        return [[NSString alloc] initWithData:jsonData encoding:NSISOLatin1StringEncoding];
    }else{
        return @"[]";
    }
}

/**
 将JSON串转化为字典或者数组

 @param jsonString JSON串
 @return 字典或数组
 */
- (id)toArrayOrNSDictionary:(NSString *)jsonString{
    NSError *error = nil;
    // 将NSString转化为NSData
    NSData *jsonData = [jsonString dataUsingEncoding:NSISOLatin1StringEncoding];
    if (!jsonData) {
        id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData
                                                        options:NSJSONReadingAllowFragments
                                                          error:&error];
        if (jsonObject != nil && error == nil){
            return jsonObject;
        }else{
            // 解析错误
            return nil;
        }
    }else{
        return nil;
    }
}

#pragma mark - URLEncode And URLDecode
/// urlencode编码
- (NSString *)URLEncodeString:(NSString *)textString
{
    return (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL,
                                                                                 (__bridge CFStringRef)textString,
                                                                                 NULL,
                                                                                 (CFStringRef)@"!*'\"();:@&=+$,/?%#[]% ",
                                                                                 CFStringConvertNSStringEncodingToEncoding(NSISOLatin1StringEncoding)));
}
/// urldecode解码
- (NSString *)URLDecodeString:(NSString *)urlEncodeString
{
    return (NSString *)CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL,
                                                                                                 (__bridge CFStringRef)urlEncodeString,
                                                                                                 CFSTR(""),
                                                                                                 CFStringConvertNSStringEncodingToEncoding(NSISOLatin1StringEncoding)));
}

@end
